(function(D){
    var relations={},
        dbIntervals = [],
        images = [],
        pageIndice = 0,
        paper,
        image,
        zoom,
        updateImage,
        getZones,
        addPath,
        paths,
        deleteZone,
        zones,
        saveZone,
        addRelation,
        annotate,
        updateAutocomplete,
        makeHtmlId,
        makeDbId,
        makeIntervals,
        makeHtml, 
        isATextNode, 
        isASpanNode,
        isAPNode,
        isAStrongNode,
        isAEmNode,
        isASubNode,
        isASupNode,
        isAInlineFormatingNode,
        loadEditor,
        save,
        nextPage,
        previousPage,
        balance,
        deleteInterval,
        linkIntervalZone,
        textFrame;

    textFrame = function(){
        return $("#transcription_ifr").contents();
    }

    makeHtmlId = function(id){
        return id.replace(/\//g, 'slash').replace(/:/g, 'colon').replace(/\./g, 'dot');
    }

    makeDbId = function(id){
        return id.replace(/slash/g, '/').replace(/colon/g, ':').replace(/dot/g, '.');
    }

    makeIntervals = function(editor){
        var body=editor.getBody(),
            nbChars=0,
            intervals=[],
            text="",
            processChilds;
        
        processChilds=function(elem, supNbChars){
            var nbChars = 0,
                subNbChars = 0;
        
            $.each(elem.childNodes, function(i,child){
                if(isATextNode(child)){
                    nbChars += child.textContent.length;
                    text += child.textContent;
                }else if(isAInlineFormatingNode(child)){
                    text += "<" + child.tagName + ">";
                    nbChars += processChilds(child, 
        			     supNbChars + 
        			     nbChars + 
        			     "<>".length + 
        			     child.tagName.length);
                    nbChars += "<>".length + "</>".length + 2*child.tagName.length;
                    text += "</" + child.tagName + ">";
        	    }else if(isASpanNode(child)){
        	        subNbChars = 0;
        	        // la suppression d'une annotation peut laisser un SPAN vide :
        	        if(child.innerHTML != ""){
        	            subNbChars = processChilds(child, supNbChars + nbChars);
        	            intervals.push({
        	                start: supNbChars + nbChars + 1,
        	                end: supNbChars + nbChars + subNbChars,
        	                class: child.className,
        	                id: child.id
        	            });
        	        }
                    nbChars += subNbChars;
        	    }
            });
            return nbChars;
        }
        $.each(body.childNodes, function(i,child){
            if(isAPNode(child)){
        	    text += "<P>";
        	    nbChars += processChilds(child, nbChars + "<P>".length);
        	    nbChars += "<P></P>".length;
        	    text += "</P>";
            }
        });
        return {
            intervals: intervals,
            text: text
        };
    }

    makeHtml = function(intervals, text){
        var offsetStart=0,
            offsetEnd=0,
            tagOpenLength = '<span class="" id="">'.length,
            tagCloseLength = '</span>'.length;

        $.each(intervals, function(i,interval){
            var previousInterval,
                offsetStart=0,
                offsetEnd=0,
                openAndCloseOffset, openOffset;

            for(var j=0; j<i; j+=1){
                previousInterval = intervals[j];
                openAndCloseOffset = previousInterval.class.length + previousInterval.id.length + 
                                     tagOpenLength + tagCloseLength;
                openOffset = previousInterval.class.length + previousInterval.id.length + tagOpenLength;
                if(interval.start > previousInterval.end){offsetStart += openAndCloseOffset;}
                else if(interval.start > previousInterval.start){offsetStart += openOffset;}
                if(interval.end > previousInterval.end){offsetEnd += openAndCloseOffset;}
                else if(interval.end > previousInterval.start){ offsetEnd += openOffset; }
            }
            text = text.slice(0,offsetStart+interval.start-1)+
                '<span class="'+interval.class+'" id="'+interval.id+'">'+
                text.slice(offsetStart+interval.start-1, offsetEnd+interval.end)+
                '</span>' +
                text.slice(offsetEnd + interval.end);
        });
        return text;
    }

    isATextNode = function(node){
        return !node.tagName;
    }
    isASpanNode = function(node){
        return (node.tagName && node.tagName === "SPAN");
    }
    isAPNode = function(node){
        return (node.tagName && node.tagName === "P");
    }
    isAStrongNode = function(node){
        return (node.tagName && node.tagName === "STRONG");
    }
    isAEmNode = function(node){
        return (node.tagName && node.tagName === "EM");
    }
    isASubNode = function(node){
        return (node.tagName && node.tagName === "SUB");
    }
    isASupNode = function(node){
        return (node.tagName && node.tagName === "SUP");
    }
    isAInlineFormatingNode = function(node){
        return (isAStrongNode(node) || isAEmNode(node) ||
            isASubNode(node) || isASupNode(node));
    }

    loadEditor = function(){
        if(tinymce.editors.length>0) tinymce.activeEditor.remove();
        tinymce.init({
            mode:"textareas",
            theme:"advanced",
            forced_root_block: 'p',
            force_br_newlines: false,
            force_p_newlines: true,
            valid_elements: "@[id|class],span,p,sub,sup,strong,em",
            plugins:"noneditable",
            theme_advanced_buttons1:"bold, italic, sub, sup",
            theme_advanced_buttons2:"",
            theme_advanced_buttons3:"",
            theme_advanced_buttons4:"",
            theme_advanced_toolbar_location:"top",
            theme_advanced_toolbar_align:"left",
            theme_advanced_statusbar_location:"bottom",
            theme_advanced_resizing:true,
            content_css:"css/relations.css",
            setup:function(editor){
                editor.onInit.add(function(editor){
                    D.request("transcription_get_text.txt",
                        {
                            "page": D.pageId
                        }, function(results){
                            var initialText = "";

                            if(results.length === 1)
                            {
                                initialText = decodeURIComponent(results[0].text.value);
                                D.request("transcription_get_intervals.txt",
                                    {
                                        "page": D.pageId
                                    }, function(results){
                                        dbIntervals = [];
                                        $.each(results, function(i,r){
                                            dbIntervals.push({
                                                id: makeHtmlId(r.interval.value),
                                                start: parseInt(r.start.value),
                                                end: parseInt(r.end.value),
                                                relation: r.relationId.value,
                                                relationLabel: r.relationLabel.value,
                                                vocab: r.vocabularyId.value,
                                                vocabLabel: r.vocabularyLabel.value,
                                                class: r.vocabularyLabel.value + "_" +
                                                r.relationLabel.value
                                            });
                                        });
                                        editor.setContent(makeHtml(dbIntervals, initialText));
                                        $.each(dbIntervals, function(i,interval){
                                            var elem=textFrame().find("#"+interval.id);
                                            elem.contextMenu({
                                                menu: 'contextMenu',
                                                offsetX: parseFloat($("#text-container").css("marginLeft")),
                                                offsetY: 35
                                            },
                                                function(action, el){
                                                    var intervalId = makeDbId(el.attr("id"));
                                                    switch(action){
                                                        case "annotate":
                                                            alert("non implémenté");
                                                            break;
                                                        case "delete":
                                                            deleteInterval(intervalId, loadEditor);       
                                                            break;
                                                        case "link":
                                                            if(zones.current && zones.current.dbId){
                                                                zones.addInterval(el.attr("id"));
                                                                linkIntervalZone(intervalId, zones.current.dbId);
                                                            }else{
                                                                alert("zone non sélectionnée ou non sauvée");
                                                            }
                                                            break;
                                                    }
                                            });
                                        });
                                    });
                            }else{
                                editor.setContent("");
                            }
                    });
                });
            }    
        });
    }

    linkIntervalZone = function(intervalId, zoneId){
        D.request("linkIntervalZone.txt", {
                interval: intervalId,
                zone: zoneId,
                user: D.userName,
                timestamp: D.timestamp()
            }, null, true);
    }

    balance = {
        c:0,
        f:false,
        inc:function(){
            this.c+=1;
        },
        dec:function(){
            this.c -= 1;
            if(this.f && this.c===0) this.f();
        },
        exec:function(fn){
            this.f=fn;
        }
    };

    save = function(){
        var relation,
            timestamp = D.timestamp(),
            editorIntervals = makeIntervals(tinymce.activeEditor),
            savedIntervals = dbIntervals,
            bal = Object.create(balance);

        $("#save").hide();
        $.each(editorIntervals.intervals, function(i,edInterval){
            var key, vocabLabel, relationLabel;

            if(edInterval.id === ""){
                relationLabel = /^(.*)_(.*)/.exec(edInterval.class)[2];
                vocabLabel = /^(.*)_/.exec(edInterval.class)[1];
                key = vocabLabel+':'+relationLabel;
                relation = relations[key].id;
                bal.inc();
                D.request("transcription_add_interval.txt",
                    {
                        page: D.pageId,
                        timestamp: timestamp,
                        start: edInterval.start,
                        end: edInterval.end,
                        relation: relation,
                        user: D.userName
                    }, function(){bal.dec();}, true);
            }else{
                $.each(savedIntervals, function(j,savedInterval){
                    if(savedInterval.id === edInterval.id){
                        if(savedInterval.start != edInterval.start ||
                           savedInterval.end   != edInterval.end){
                            (function(){
                                var timestampbis = timestamp;
                                bal.inc();
                                D.request("updateInterval1.txt",
                                    {
                                        intervalId: makeDbId(edInterval.id)
                                    }, 
                                    function(){
                                        D.request("updateInterval2.txt",
                                            {
                                                intervalId: makeDbId(edInterval.id),
                                                start: edInterval.start,
                                                end: edInterval.end,
                                                timestamp: timestampbis,
                                                user: D.userName
                                            }, function(){bal.dec();}, true);
                                    }, true);
                            })();
                        }
                        savedIntervals.remElem(savedInterval);
                        return false;
                    }
                });
            }
            timestamp += 1;
        });
        $.each(savedIntervals, function(i,savInterval){
            var timestampbis = timestamp,
                id = makeDbId(savInterval.id);

            bal.inc();
            deleteInterval(id, function(){bal.dec();}, timestampbis);
            timestamp += 1;
        });
        bal.inc();
        bal.exec(function(){ $("#save").show(); loadEditor(); });
        D.request("transcription_delete_text.txt",
            {
                page: D.pageId
            }, function(){
                D.request("transcription_add_text.txt",
                    {
                        page: D.pageId,
                        text: encodeURIComponent(editorIntervals.text),
                        user: D.userName,
                        timestamp: timestamp + 1
                    }, function(){bal.dec();}, true);
            }, true);
    }

    deleteInterval = function(id, f, argTimestamp){
        var timestamp;
        if(argTimestamp){ timestamp=argTimestamp; }else{ timestamp = D.timestamp(); }
        D.request("deleteInterval1.txt",
            {
                "intervalId": id
            }, function(){
                D.request("deleteInterval2.txt",
                    {
                        "intervalId": id,
                        "user": D.userName,
                        "timestamp": timestamp
                    }, f, true);
            }, true);
    }

    nextPage = function(){
	    if(pageIndice != (images.length - 1)){
	        pageIndice += 1;
	        D.pageId = images[pageIndice].id;
	        updateImage();
	        loadEditor();
	    }
    }

    previousPage = function(){
	    if(pageIndice != 0){
	        pageIndice -= 1;
	        D.pageId = images[pageIndice].id;
	        updateImage();
	        loadEditor();
	    }
    }

    addPath = function(path, translatedPath){
	    var pathElem;

	    pathElem = paper.path(path).
	        scale(zoom,zoom,0,0).
	        animate({
	    	"stroke-width": 3,
	    	"stroke": "#aaa"
	        }, 1000, function(){
	    	    this.hide();
	        });
	    if(translatedPath) pathElem.strPath = translatedPath;
	    paths.push(pathElem);
	    return pathElem;
    }

    getZones = function(){
        D.request("getZones.txt",
		    {
		        "page": D.pageId
		    }, function(results){
		        var path;

		        $.each(results, function(i,r){
                    var set = $.grep(paths.items, function(item){ 
                                return (item.dbId && item.dbId === r.zoneId.value);
                              });
                    if(r.intervalId && set.length === 1){
                        set[0].intervals.push(makeHtmlId(r.intervalId.value));
                    }else{
                        path = addPath(r.path.value);
                        path.dbId = r.zoneId.value;
                        if(r.intervalId){
                            path.intervals = [makeHtmlId(r.intervalId.value)];
                        }else{
                            path.intervals = [];
                        }
                    }
		        });
		    });
    }

    updateImage = function(){
        var tempPoints = paper.set(),
            path = "",
            translatedPath = "";

        paths = paper.set();
        if(image) image.remove();
        image = paper.image(images[pageIndice].big, 0, 0, images[pageIndice].bigWidth, images[pageIndice].bigHeight);
        zoom = 0.4;
        image.scale(zoom,zoom,0,0);

        $(image.node).mousedown(function(e){
            var x0 = e.pageX,
                y0 = e.pageY,
                point,
                newPoint,
                strxy,
                pathElem;


            e.preventDefault();
            switch(e.which){
            case 1:
                $("body").mousemove(function(e){
                    var x1 = e.pageX,
                        y1 = e.pageY,
                        deltaX = x1-x0, deltaY = y1-y0;
                    
                    image.translate(deltaX,deltaY);
                    tempPoints.translate(deltaX,deltaY);
                    paths.translate(deltaX,deltaY);
                    x0 = x1;
                    y0 = y1;
                });
                $("body").mouseup(function(e){
                    $("body").unbind('mousemove');
                    $("body").unbind('mouseup');
                });
                break;
            case 3:
                newPoint = function(){
                    return paper.circle(e.pageX, e.pageY, 5).animate({"fill": "#fff"}, 1000);
                }

                strxy = function(translation){
                    var result, x, y;

                    if(!translation) translation = {x: 0, y: 0};
                    x = (point.attr("cx")-translation.x)*(1/zoom);
                    y = (point.attr("cy")-translation.y)*(1/zoom);
                    result = x + " " + y;
                    return result;
                }

                point = newPoint();
                tempPoints.push(point);
                if(tempPoints.length === 1){
                    path += "M " + strxy();
                    translatedPath += "M " + strxy(image.attr("translation"));
                    $(point.node).mousedown(function(){
                        path += " Z";
                        translatedPath += " Z";
                        pathElem = addPath(path, translatedPath);
                        pathElem.intervals = [];
                        path = "";
                        translatedPath = "";
                        tempPoints.remove();
                        tempPoints = paper.set();
                    });
                }else{
                    path += " L " + strxy();
                    translatedPath += " L " + strxy(image.attr("translation"));
                }
                break;
            }
        });
        getZones();
    }

    saveZone = function(strPath){
        var timestamp = D.timestamp(),
            zoneId = D.pageId + "/zones/" + timestamp;

        D.request("addZone.txt",
            {
                page: D.pageId,
                path: strPath,
                timestamp: timestamp,
                user: D.userName,
                zoneId: zoneId
            }, null, true);
        return zoneId;
    }

    deleteZone = function(zoneId){
        D.request("deleteZone1.txt",
            {
                zone: zoneId
            }, function(){
                D.request("deleteZone2.txt",
                    {
                        zone: zoneId,
                        user: D.userName,
                        timestamp: D.timestamp()
                    }, null, true);
            }, true);
    }

    zones = {
        zoneIndice:-1,
        zone: function() { return paths.items[zones.zoneIndice]; },
        current:false,
        fadeouts: [],
        addInterval: function(intervalId){
            if(zones.current){
                zones.zone().intervals.push(intervalId);
            }
        },
        next:function(){
            var zone; 

            zones.zoneIndice += 1;
            zone = zones.zone();

            $.each(zones.fadeouts, function(i,fadeout){
                fadeout();
            });
            zones.fadeouts = [];
            paths.hide();
            $("#deleteZone").hide();
            $("#saveZone").hide();
            if(zone){
                if(zone.removed){
                    zones.zoneIndice += 1;
                    zones.next();
                    return;
                }
                zones.current=zone;
                zone.show();
                $.each(zone.intervals, function(i,interval){
                    var e = textFrame().find("#"+interval),
                        c;
                        
                    if(e.length){    
                        c = e.css("backgroundColor");
                        e.css('backgroundColor', '#aaaacc');
                        zones.fadeouts.push(function(){ 
                            e.css('backgroundColor', c);
                        })
                    }
                });
                $("#deleteZone").show();
                $("#deleteZone").unbind('click');
                if(zone.dbId){
                    $("#deleteZone").click(function(){
                        deleteZone(zone.dbId);
                        zone.remove();
                        $("#deleteZone").hide();
                    });
                }else{
                    $("#saveZone").show();
                    $("#saveZone").unbind('click');
                    $("#deleteZone").click(function(){
                        zone.remove();
                        $("#deleteZone").hide();
                        $("#saveZone").hide();
                    });
                    $("#saveZone").click(function(){
                        var zoneId = saveZone(zone.strPath);

                        zone.dbId = zoneId;
                        $("#saveZone").hide();
                    });
                }
            }else{
                zones.current=false;
                zones.zoneIndice = -1;
            }
        }
    };

    addRelation = function(item){
        var vocabularyLabel, relationLabel, split, vocabularyId=false, 
            makeRelation, encodedVocabularyLabel, encodedRelationLabel, timestamp;

        makeRelation = function(){
            D.request("addRelation.txt",
                {
                    vocabId: vocabularyId,
                    relationLabel: encodedRelationLabel,
                    user: D.userName,
                    timestamp: timestamp + 1
                }, null, true);
            relations[item]={
                id: vocabularyId + '#' + encodedRelationLabel,
                label: relationLabel,
                vocabularyId: vocabularyId,
                vocabularyLabel: vocabularyLabel
            };
            updateAutocomplete();
        }
        if(!confirm("ajouter la relation " + item + " ?")) return;
        split = item.split(":");
        if(split.length != 2){alert("relation mal nommée"); return;}
        vocabularyLabel = split[0].toLowerCase();
        relationLabel = split[1].toLowerCase();
        encodedVocabularyLabel = encodeURIComponent(vocabularyLabel);
        encodedRelationLabel = encodeURIComponent(relationLabel);
        timestamp = D.timestamp();
        $.each(relations, function(label, r){
            if(r.vocabularyLabel === vocabularyLabel){
                vocabularyId = r.vocabularyId;
                return false;
            }
        });
        if(!vocabularyId){
            vocabularyId = 'http://desanti.org/schemas/'+encodedVocabularyLabel;
            D.request("addVocabulary.txt",
                {
                    vocabLabel: encodedVocabularyLabel,
                    vocabId: vocabularyId,
                    user: D.userName,
                    timestamp: timestamp
                },makeRelation, true);
        }else{ makeRelation(); }
    }

    annotate = function(){
        var item = $("#relations").val(),
            r = relations[item],
            s = tinymce.activeEditor.selection,
            c = s.getContent(),
            e = encodeURIComponent;

        if(!r || c==="") return;
        s.setContent('<span class="'+e(r.vocabularyLabel)+'_'+e(r.label)+'">'+c+'</span>');
    }

    updateAutocomplete = function(){
        var autocompleteList = [];

        $.each(relations, function(item){
            autocompleteList.push(item);
        });
        D.autocomplete(autocompleteList, 
            document.getElementById("relations"),
            function(item){
                if(!relations[item]){
                    addRelation(item);
                }
            });
    }

    $(document).ready(function(){
        // L'identifiant de la page visualisée est passé en paramètre de l'URL
        D.pageId = location.href.split( "?" )[ 1 ].split( "&" )[ 0 ].split( "=" )[ 1 ];
        
        // L'identifiant de la collection à laquelle appartient la page
        // est passé en paramètre de l'URL
        D.collectionId = location.href.split( "?" )[ 1 ].split( "&" )[ 1 ].split( "=" )[ 1 ];
        
        D.userName = self.opener.DESANTI.userName;

        images = self.opener.DESANTI.transcriptionLine.images;

        $.each(images, function(i,im){
            if(im.id === D.pageId){
                pageIndice = i;
                return false;
            }
        });

        paper = Raphael(0, 0, D.getDocWidth()/(1.6), D.getDocHeight());
        updateImage();

        $(document).bind("contextmenu", function(e){ return false; });

        $("#zoomPlus").button();
        $("#zoomMinus").button();
        $("#zoomPlus").click(function(){
            zoom += 0.05;
            image.scale(zoom,zoom,0,0);
            paths.scale(zoom,zoom,0,0);
        });
        $("#zoomMinus").click(function(){
            if(zoom > 0.1){
                zoom -= 0.05;
                image.scale(zoom,zoom,0,0);
                paths.scale(zoom,zoom,0,0);
            }
        });
        $("#save").button();
        $("#save").click(function(){
            save();
        });

        $("#previousPage").button();
        $("#previousPage").click(previousPage);
        $("#nextPage").button();
        $("#nextPage").click(nextPage);
        $("#zones").button();
        $("#zones").click(function(){zones.next();});
        $("#deleteZone").button().hide();
        $("#saveZone").button().hide();
        $("#annotate").button();
        $("#annotate").click(annotate);
        
        loadEditor();
        
        D.request("transcription_get_vocabularies.txt",{},function(results){
            $.each(results, function(i,r){
                var vocab = r.v.value,
                    lastVocab = (i===results.length-1);

                D.request("transcription_get_relations.txt",
                    {
                        "vocabulary": vocab
                    }, function(results2){
                        $.each(results2, function(j,r2){
                            var relationLabel, vocabularyLabel;

                            relationLabel = decodeURIComponent(r2.rLabel.value);
                            vocabularyLabel = decodeURIComponent(r2.vLabel.value);
                            relations[vocabularyLabel+':'+relationLabel]={
                                id: r2.r.value,
                                label: relationLabel,
                                vocabularyId: vocab,
                                vocabularyLabel: vocabularyLabel
                            };
                        });
                        if(lastVocab) updateAutocomplete();
                    });
            });
        });
    });
})(DESANTI);
